// Parser2.cpp :	Defines the entry point for the console application and read source code from file.
//					Parse SIMPLE source code into AST like structure.
//					Insert into relevant VarTable, ProcTable.
// Author: Heng Belson
//
#include	"Parser2.h"
#include	"SPA_Lexical.h"
#include	"../PKB/AST/AST_Builder.h"
#include	"../PKB/TABLES/VarTable.h"
#include	"../PKB/TABLES/ProcTable.h"
#include	"../PKB/TABLES/StatTable.h"
#include	<fstream>
#include	<iostream>
#include	<boost/algorithm/string.hpp>

/******************************/
// Global Declarations		   
/******************************************************************************************************************/
ProcTable	*procTable;
VarTable	*varTable;
StatTable	*statTable;

AST_Builder	*builder;
SPA_Lexical lex;
INTEGER		totalStmts;
ofstream	logger("Parser2.log");
ofstream	parsed("PSource.txt");
BOOLEAN		nested;
/******************************************************************************************************************/

#pragma region Constructor
Parser2::Parser2() {
	lex			=	SPA_Lexical();
	procTable	=	ProcTable::getFirstInstance();
	varTable	=	VarTable::getFirstInstance();
	statTable	=	StatTable::getFirstInstance();
	builder		=	AST_Builder::getInstance();
	totalStmts	=	0;
	nested		=	false;
}
#pragma endregion

#pragma region Main Functions Implementation
/******************************/
/* Function Name: Parse
/* Description	: Take in path to the source file to parse
/******************************************************************************************************************/
STATUS Parser2::parse(STRING readFromFile) {
	lex.scanner(readFromFile);	
	return program();
}
/******************************************************************************************************************/

/******************************/
/* Function Name: Program
/* Description	: For program node
/******************************************************************************************************************/
#pragma region Program
STATUS Parser2::program() {
	procedure();
	statTable->addNoofStmts(totalStmts);
	return STATUS_Success;
}
#pragma endregion
/******************************************************************************************************************/

/******************************/
/* Function Name: Procedure
/* Description	: For procedure node
/******************************************************************************************************************/
#pragma region Procedure
void Parser2::procedure() {
	STRING proc_name, proc_body, next_proc_definition;
	INTEGER totalProcedures		=	lex.getTotalProcedures();

	for (; totalProcedures > 0; totalProcedures--) 
	{
		next_proc_definition	=	lex.getNextProcedure();

		lex.match(next_proc_definition, lex.G_Procedure);
		lex.analyze(SPA_Lexical::CONSTRUCT_Procedure);
		proc_name = lex.getProcName();
		proc_body = lex.getProcBody();

		/////////////////////////////////////////////////////////////////
		logger << "******************************************" << endl;
		logger << "Proc Name: " << proc_name << endl;
		logger << "******************************************\n" << endl;

		parsed << lex.TK_Procedure << " " << proc_name << " {" << endl;
		/////////////////////////////////////////////////////////////////

		PROC_INDEX proc_id	= procTable->insertProc(proc_name);
		builder->addProcedure(proc_id);

		statementList(proc_body);
		
		/////////////////////////////////////////////////////////////////
		logger << "******************************************" << endl;
		logger << "End Procedure" << endl;
		logger << "******************************************\n" << endl;

		parsed << "}" << endl;
		/////////////////////////////////////////////////////////////////
	}
}
#pragma endregion
/******************************************************************************************************************/

/******************************/
/* Function Name: StatementList
/* Description	: For statement list node
/******************************************************************************************************************/
#pragma region Statement List
void Parser2::statementList(STRING stmtLst) {
	COLLECTION statement_list_block;

	lex.match(stmtLst, lex.G_StmtLst);
	lex.analyze(SPA_Lexical::CONSTRUCT_Statement_List);
	statement_list_block = lex.getStmtLstBlock();

	for (INTEGER i = 0; i < (INTEGER) statement_list_block.size(); i++) 
	{
		/////////////////////////////////////////////////////////////////
		STRING format = nested ? ".\t\t" : ".\t";
		parsed << ++totalStmts << format;
		/////////////////////////////////////////////////////////////////

		statement(statement_list_block[i]);
	}
}
#pragma endregion
/******************************************************************************************************************/

/******************************/
/* Function Name: Statement
/* Description	: For statement node (If, While, Call, Assign)
/******************************************************************************************************************/
#pragma region Statement
void Parser2::statement(STRING stmt) {
	STATEMENT_TYPE statement_type;

	lex.match(stmt, lex.G_Stmt);
	lex.analyze(SPA_Lexical::CONSTRUCT_Statement);
	statement_type = lex.getStmtType();

	switch (statement_type) {
	case STATEMENT_Call:
		{
			STRING		proc_name;
			PROC_INDEX	call_id;

			proc_name		=	lex.getCall();
			call_id			=	procTable->insertProc(proc_name);

			builder->addCall(call_id);

			/////////////////////////////////////////////////////////////////
			logger << "Call: " << proc_name << "\n" << endl;
			parsed << lex.TK_Call << " " << proc_name << ";" << endl;
			/////////////////////////////////////////////////////////////////

			break;
		}
	case STATEMENT_While:
		{
			STRING		condition_var, body;
			VAR_INDEX	condition_id;

			condition_var	=	lex.getWhileCondition();
			body			=	lex.getWhileBody();

			condition_id	=	varTable->insertVar(condition_var,  NUMERIC_EXPR_TYPE_VARIABLE);

			builder->beginWhile(condition_id);

			/////////////////////////////////////////////////////////////////
			logger << "While condition: " << condition_var << endl;
			parsed << lex.TK_While << " " << condition_var << " {" << endl;
			/////////////////////////////////////////////////////////////////

			nested = true;
			statementList(body);
			nested = false;

			/////////////////////////////////////////////////////////////////
			logger << "End while " << "\n" << endl;
			parsed << "\t}" << endl;
			/////////////////////////////////////////////////////////////////

			builder->endWhile();

			break;
		}
	case STATEMENT_If:
		{
			STRING		condition_var, then_part, else_part;
			VAR_INDEX	condition_id;

			condition_var	=	lex.getIfCondition();
			then_part		=	lex.getIfThenPart();
			else_part		=	lex.getIfElsePart();

			condition_id	=	varTable->insertVar(condition_var,  NUMERIC_EXPR_TYPE_VARIABLE);
			
			builder->beginIf(condition_id);

			/////////////////////////////////////////////////////////////////
			logger << "If condition: " << condition_var << endl;
			parsed << lex.TK_If << " " << condition_var;
			/////////////////////////////////////////////////////////////////

			/////////////////////////////////////////////////////////////////
			logger << "Then: " << endl;
			parsed << " " << lex.TK_Then << " {" << endl;
			/////////////////////////////////////////////////////////////////

			nested = true;
			statementList(then_part);
			builder->endIf(if_stmtNode::THEN_BLOCK);
			nested = false;

			/////////////////////////////////////////////////////////////////
			logger << "Else: " << endl;
			parsed << "} " << lex.TK_Else << " {" << endl;
			/////////////////////////////////////////////////////////////////
			
			nested = true;
			statementList(else_part);
			builder->endIf(if_stmtNode::ELSE_BLOCK);
			nested = false;
			
			/////////////////////////////////////////////////////////////////
			logger << "End if " << "\n" << endl;
			parsed << "\t}" << endl;
			/////////////////////////////////////////////////////////////////
			break;
		}
	case STATEMENT_Assign:
		{
			STRING		left_variable, expr, instr;
			VAR_INDEX	var_id;

			left_variable	=	lex.getAssignLeftVar();
			expr			=	lex.getAssignExpr();

			var_id			=	varTable->insertVar(left_variable, NUMERIC_EXPR_TYPE_VARIABLE);
			builder->addAssign(var_id);

			/////////////////////////////////////////////////////////////////
			logger << "Assignment: " << "\n" << endl;
			logger << "\tLeft Var: " << left_variable << endl;
			logger << "\tExpression: " << expr << "\n" << endl;

			parsed << left_variable << " = " << lex.getOriginalExpr() << ";" << endl;
			/////////////////////////////////////////////////////////////////

			expression(expr, BRANCH_Root, (lex.isReducible(expr)) ? false : true);
			break;
		}
	default: 
			cerr << "Unknown statement type: " << statement_type; system("PAUSE"); exit(-1);
			break;
	}
}
#pragma endregion
/******************************************************************************************************************/

/******************************/
/* Function Name: Expression
/* Description	: For compound expression
/******************************************************************************************************************/
#pragma region Expression
void Parser2::expression(STRING expr, BRANCH_TYPE branchType, BOOLEAN isFactor) {
	for (INTEGER i = 0; i < (INTEGER) expr.length(); i++) 
	{
		if (expr[i] == '*' || expr[i] == '+' || expr[i] == '-') 
		{
			//////////////////////////////////////////////////
			logger << "\t\t\tBEGIN " << expr[i] << endl;
			//////////////////////////////////////////////////
			builder->beginExpr((expr[i] == '*') ? OP_Times : (expr[i] == '+') ? OP_Plus : OP_Minus);
		}
		else if (expr[i] == '[') 
		{
			INTEGER j  = 0;
			STRING tmp, operands;

			tmp = expr;
			tmp.erase(0,2); tmp.erase(tmp.length()-1,1);
			operands = tmp;
			
			//////////////////////////////////////////////////
			if (lex.isOperator(operands[0]) && (INTEGER) operands.find("[") > -1) 
			{
				INTEGER openBrace = 0, closeBrace = 0;
				for (; j < (INTEGER) operands.length(); j++) 
				{
					if (operands[j] == '[')
						openBrace++;
					if (operands[j] == ']')
						closeBrace++;
					if (operands[j] == ',' && openBrace != 0 && openBrace == closeBrace)
						break;
				}
			}
			else
				j = (INTEGER) operands.find(",");
			//////////////////////////////////////////////////

			STRING operand1 = operands.substr(0, j);
			STRING operand2 = operands.substr(j+1, operands.length()-j);

			expression(operand1, BRANCH_Left, false);
			expression(operand2, BRANCH_Right, false);

			i += operand1.length() + operand2.length() + 1;
		}
		else if (expr[i] == ']') 
		{
			if (branchType == BRANCH_Root || branchType == BRANCH_Right) {
				builder->endExpr(composite_expr::ADD_TO_RHS);
				//////////////////////////////////////////////////
				logger << "\t\t\tEND " << " Add to Right" << endl;
				//////////////////////////////////////////////////
			}
			if (branchType == BRANCH_Left) {
				builder->endExpr(composite_expr::ADD_TO_LHS);
				//////////////////////////////////////////////////
				logger << "\t\t\tEND " << " Add to Left" << endl;
				//////////////////////////////////////////////////
			}
		}
		else if (lex.match(expr, lex.T_Var_Name)) 
		{
			VAR_INDEX var_id	=	varTable->insertVar(expr, NUMERIC_EXPR_TYPE_VARIABLE);
			if (isFactor) {
				builder->addNumericExpr(NUMERIC_EXPR_TYPE_VARIABLE, var_id);
				//////////////////////////////////////////////////
				logger << "\t\t\tAdd Numeric Expr (Variable) : " << expr << endl;
				//////////////////////////////////////////////////
			}
			else 
			{
				if (branchType == BRANCH_Left) 
				{
					builder->setLeftBranch(NUMERIC_EXPR_TYPE_VARIABLE, var_id);
					//////////////////////////////////////////////////
					logger << "\t\t\tSet Left Branch (Variable) : " << expr << endl;
					//////////////////////////////////////////////////
				}

				if (branchType == BRANCH_Right) 
				{
					builder->setRightBranch(NUMERIC_EXPR_TYPE_VARIABLE, var_id);
					//////////////////////////////////////////////////
					logger << "\t\t\tSet Right Branch (Variable) : " << expr << endl;
					//////////////////////////////////////////////////
				}
			}
			i += expr.length();
		}
		else if (lex.match(expr, lex.T_Const_Value)) 
		{
			VAR_INDEX var_id	=	varTable->insertVar(expr, NUMERIC_EXPR_TYPE_CONSTANT);
			if (isFactor) 
			{
				builder->addNumericExpr(NUMERIC_EXPR_TYPE_CONSTANT, var_id);
				//////////////////////////////////////////////////
				logger << "\t\t\tAdd Numeric Expr (Constant) : " << expr << endl;
				//////////////////////////////////////////////////
			}
			else 
			{
				if (branchType == BRANCH_Left) 
				{
					builder->setLeftBranch(NUMERIC_EXPR_TYPE_CONSTANT, var_id);
					//////////////////////////////////////////////////
					logger << "\t\t\tSet Left Branch (Constant) : " << expr << endl;
					//////////////////////////////////////////////////
				}

				if (branchType == BRANCH_Right) 
				{
					builder->setRightBranch(NUMERIC_EXPR_TYPE_CONSTANT, var_id);
					//////////////////////////////////////////////////
					logger << "\t\t\tSet Right Branch (Constant) : " << expr << endl;
					//////////////////////////////////////////////////
				}
			}
			i += expr.length();
		}
		else { cerr << "Unknown expression: " << expr; system("PAUSE"); exit(-1); }
	}
}
#pragma endregion
/******************************************************************************************************************/
#pragma endregion